--
-- VHDL Architecture Fietscomputer_lib.fc_besturen.behavioural
--
-- Created:
--					by - 10083839.UNKNOWN (DTP7802)
--					at - 14:05:01 26-09-2011
--
-- using Mentor Graphics HDL Designer(TM) 2008.1b (Build 7)
--
library ieee;
use ieee.std_logic_1164.all;

-- Package which makes it easier to display certain rows
-- in the LCD ROM
package display_control is
	-- Rom table row locations
	constant romtable_num_rows : natural := 23;
	
	constant loading_location : natural := 10;	
	
	constant mainmenu_romtable_start : natural := 4;
	constant mainmenu_romtable_end : natural := 6;
	
	constant tripsmenu_romtable_start : natural := 11;
	constant tripsmenu_romtable_end : natural := 13;
	
	constant tripspeed_location : natural := 15;
	constant trip_length_location : natural := 17;
	constant trip_time_location : natural := 19;
	
	constant serialnumber_location : natural := 21;
	
	constant nav_all_location : natural := 7;
	constant nav_right_location : natural := 8;
	constant nav_left_location : natural := 9;
	
	procedure display_row(constant location : in natural; variable row_req : inout std_logic_vector(romtable_num_rows - 1 downto 0)); 
end display_control;

package body display_control is
	procedure display_row(constant location : in natural; variable row_req : inout std_logic_vector(romtable_num_rows - 1 downto 0)) is
	begin
		row_req(location) := '1';
	end procedure;
end display_control;

library ieee;
library Fietscomputer_lib;
use Fietscomputer_lib.display_control.all;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity fc_besturen is
	port( 
		switch_l : in		 std_logic;
		switch_m : in		 std_logic;
		switch_r : in		 std_logic;
		divider_ready : in std_logic;
		clk			: in std_logic;
		rst			: in std_logic;
		
		divider_sel : out std_logic_vector(2 downto 0);
		row_req	: out std_logic_vector (22 downto 0) := (others => '0');
		trip_start : out std_logic;
		trip_end : out std_logic;
		set_wheelsize : out std_logic;
		wheelsize : out natural range 0 to 28;
		km_counter_start : out std_logic
	);

-- Declarations

end fc_besturen ;

--
architecture behavioural of fc_besturen is
	-- Signals for double synchronization
	signal switch_l_sync : std_logic;
	signal switch_m_sync : std_logic;
	signal switch_r_sync : std_logic;
	
	-- Define a new type for the statemachine, which allows
	-- us to check in which menu we currently are
	type menustate is (reset, loading, wait_for_divider, 
						display_current_speed, display_total_length, 
						enter_wheelsize, enter_wheelsize2, enter_wheelsize3, enter_wheelsize4, wheelsize_set,
						start_speed_div, start_total_length_div, start_trip_speed_div, start_trip_length_div, start_trip_time_div,
						wait_for_mainmenu, display_mainmenu, wait_for_action, 
						display_tripsmenu, trips_wait_for_action, display_trip_speed, display_trip_length, display_trip_time,
						wait_for_tripdata_action, wait_for_serialno_action, display_serial_number
	);
	
	-- And a new signal which holds the current state
	signal state : menustate;
	
	-- Signals which holds the current visible menu item
	signal active_row : natural range 0 to romtable_num_rows;
	
	-- 10 kHz clock so counting to 10000 takes a second
	signal timer : natural range 0 to 10000;
	
	-- Counts how many times the middle button is pressed in the first second
	signal counter : natural range 0 to 7;
	
	-- Signal used to store the combination of key presses used to determine which wheelsize
	signal wheelsize_combo : std_logic_vector(3 downto 0);
	
	-- Signal used to store the current state of a trip (started, ended)
	signal trip_active : std_logic;
	
	-- When the divider is ready with the devision, this signal stores
	-- the state it should go to
	signal nextstate : menustate;
begin
	process(clk, rst)
		variable entry_pos : natural;
		variable rows_to_req : std_logic_vector(row_req'length - 1 downto 0) := (others => '0');
	begin
		
		if rst = '1' then
			switch_l_sync <= '0';
			switch_m_sync <= '0';
			switch_r_sync <= '0';
			row_req <= (others => '0');
			timer <= 0;
			active_row <= 0;
			set_wheelsize <= '0';
			wheelsize <= 0;
			trip_start <= '0';
			trip_end <= '0';
			trip_active <= '0';
			divider_sel <= (others => '0');
			km_counter_start <= '0';
			state <= reset;
			nextstate <= reset;
			divider_sel <= "000";
			
		elsif rising_edge(clk) then
			-- synchronize switches			
			switch_l_sync <= switch_l;
			switch_m_sync <= switch_m;
			switch_r_sync <= switch_r;
			
			-- Default no new rows to display
			rows_to_req := (others => '0');
			
			set_wheelsize <= '0';
			wheelsize <= 0;
			trip_start <= '0';
			trip_end <= '0';		
			
			case state is
				when reset =>
					-- Display loading message
					display_row(loading_location, rows_to_req);
					state <= loading;
				
				when loading =>
					
					if timer >= 10000 then
						-- We've waited 1 second
						state <= start_speed_div;
						timer <= 0;
					else
						timer <= timer + 1;
						
						if timer = 1 then
						  set_wheelsize <= '1';
						  wheelsize <= 28;
						end if;
						
						-- Check if the middle button is pressed
						if switch_m_sync = '1' then
							counter <= counter + 1;
						end if;
						
						-- And when two or more times pressed, go to enter_wheelsize state
						if counter >= 2 then
							state <= enter_wheelsize;
							timer <= 0;
							counter <= 0;
						end if;
					end if;
				
				when wait_for_divider =>
					if divider_ready = '1' then
						state <= nextstate;
					end if;
				--
				-- Main
				-- ~~~~
				--					
				when start_speed_div =>
					km_counter_start <= '0';
					divider_sel <= "001"; 
					nextstate <= display_current_speed;
					state <= wait_for_divider;
					active_row <= 0;
				
				when display_current_speed =>
					--
					-- Display first two entries in rom table
					--
					display_row(0, rows_to_req);
					display_row(1, rows_to_req);
					
					state <= wait_for_mainmenu;
					
				when start_total_length_div =>
					km_counter_start <= '1';
					divider_sel <= "000";
					nextstate <= display_total_length;
					state <= wait_for_divider;
					active_row <= 1;

				when display_total_length =>
					display_row(2, rows_to_req);
					display_row(3, rows_to_req);
					
					state <= wait_for_mainmenu;
				
				when wait_for_mainmenu =>
					if switch_l_sync = '1' then
						if active_row = 1 then
							state <= start_speed_div;
						end if;
					end if;
				
					if switch_m_sync = '1' then
						active_row <= 0;
						state <= display_mainmenu;
					end if;
					
					if switch_r_sync = '1' then
						if active_row = 0 then
							state <= start_total_length_div;
						end if;
					end if;
					
				--
				-- Enter wheelsize
				-- ~~~~~~~~~~~~~~~
				--
				when enter_wheelsize =>
					if switch_l_sync = '1' then
						wheelsize_combo(3) <= '1';
						state <= enter_wheelsize2;
					elsif switch_r_sync = '1' then
						wheelsize_combo(3) <= '0';
						state <= enter_wheelsize2;
					elsif switch_m_sync = '1' then
						state <= start_speed_div;
					end if;
				
				when enter_wheelsize2 =>
					if switch_l_sync = '1' then
						wheelsize_combo(2) <= '1';
						state <= enter_wheelsize3;
					elsif switch_r_sync = '1' then
						wheelsize_combo(2) <= '0';
						state <= enter_wheelsize3;
					elsif switch_m_sync = '1' then
						state <= start_speed_div;
					end if;
					
				when enter_wheelsize3 =>
					if switch_l_sync = '1' then
						wheelsize_combo(1) <= '1';
						state <= enter_wheelsize4;
					elsif switch_r_sync = '1' then
						wheelsize_combo(1) <= '0';
						state <= enter_wheelsize4;
					elsif switch_m_sync = '1' then
						state <= start_speed_div;
					end if;
				
				when enter_wheelsize4 =>
					if switch_l_sync = '1' then
						wheelsize_combo(0) <= '1';
						state <= wheelsize_set;
					elsif switch_r_sync = '1' then
						wheelsize_combo(0) <= '0';
						state <= wheelsize_set;
					elsif switch_m_sync = '1' then
						state <= start_speed_div;
					end if;
				
				when wheelsize_set =>
					set_wheelsize <= '1';
					
					case wheelsize_combo is
						when "0000" => wheelsize <= 14;
						when "0001" => wheelsize <= 16;
						when "0010" => wheelsize <= 18;
						when "0011" => wheelsize <= 20;
						when "0100" => wheelsize <= 22;
						when "0101" => wheelsize <= 24;
						when "0110" => wheelsize <= 26;
						when "0111" => wheelsize <= 27;
						when "1000" => wheelsize <= 28;
						when others => null;
					end case;
					
					state <= start_speed_div;
							
				--
				-- Main Menu
				-- ~~~~~~~~~
				--
				when display_mainmenu =>					
					-- Check what we should display on the bottom row of the LCD
					if active_row = 0 then
						entry_pos := nav_right_location;
					elsif active_row = (mainmenu_romtable_end - mainmenu_romtable_start) then
						entry_pos := nav_left_location;
					else
						entry_pos := nav_all_location;
					end if;		
					
					-- Display current menu item
					display_row(mainmenu_romtable_start + active_row, rows_to_req);
					
					-- Display the right navigation options on the bottom row					
					display_row(entry_pos, rows_to_req);
					state <= wait_for_action;
					
					-- KM counter can stop calculating
					km_counter_start <= '0';				
					
				when wait_for_action =>					
					if switch_l_sync = '1' then
						if active_row > 0 then
							-- Decrement current active menu item
							active_row <= active_row - 1;
							
							-- And go to display
							state <= display_mainmenu;
						end if;
					end if;
					
					if switch_r_sync = '1' then			
						if active_row < (mainmenu_romtable_end - mainmenu_romtable_start) then
							-- Increment current active menu item
							active_row <= active_row + 1;
							
							-- And go to display
							state <= display_mainmenu;
						end if;
					end if;
					
					if switch_m_sync = '1' then
						case active_row is
							when 0 =>
								-- First menu item is 'Back'
								-- So go back to begin state
								active_row <= 0;
								state <= start_speed_div;
							
							when 1 =>
								-- Second item is trips
								active_row <= 0;
								state <= display_tripsmenu;
							
							when 2 =>
								-- Third item is bike data
								active_row <= 0;
								state <= display_serial_number;
							
							when others =>
								state <= display_mainmenu;
						end case;
					end if;
				
				--
				-- Trips menu
				-- ~~~~~~~~~~
				--				
				when display_tripsmenu =>
					-- Check what we should display on the bottom row of the LCD
					if active_row = 0 then
						entry_pos := nav_right_location;
					elsif active_row = (tripsmenu_romtable_end - tripsmenu_romtable_start) then
						entry_pos := nav_left_location;
					else
						entry_pos := nav_all_location;
					end if;

					-- Check if trip start/end item is current menu item
					if active_row = 1 then
						-- Display the right entry (end or start)
						if trip_active = '1' then
							display_row(tripsmenu_romtable_start + active_row + 2, rows_to_req);
						else
							display_row(tripsmenu_romtable_start + active_row, rows_to_req);
						end if;
					else
						display_row(tripsmenu_romtable_start + active_row, rows_to_req);
					end if;
					
					-- Display the right navigation options on the bottom row					
					display_row(entry_pos, rows_to_req);
					state <= trips_wait_for_action;
				
				when trips_wait_for_action =>
					if switch_l_sync = '1' then
						if active_row > 0 then
							-- Decrement current active menu item
							active_row <= active_row - 1;
							
							-- And go to display
							state <= display_tripsmenu;
						end if;
					end if;
					
					if switch_r_sync = '1' then			
						if active_row < (tripsmenu_romtable_end - tripsmenu_romtable_start) then
							-- Increment current active menu item
							active_row <= active_row + 1;
							
							-- And go to display
							state <= display_tripsmenu;
						end if;
					end if;
					
					if switch_m_sync = '1' then
						case active_row is
							when 0 =>
								state <= display_mainmenu;
							
							when 1 =>
								-- Enable/disable trip
								if trip_active = '1' then
									trip_active <= '0';
									trip_end <= '1';
								else
									trip_active <= '1';
									trip_start <= '1';
								end if;
								
								-- Redraw tripsmenu
								state <= display_tripsmenu;
							
							when 2 =>
								-- View tripdata
								active_row <= 0;
								state <= start_trip_speed_div;
							
							when others =>
								state <= display_tripsmenu;
						end case;
					end if;

				when start_trip_speed_div =>
					-- Select right channel for divider
					divider_sel <= "100";
					nextstate <= display_trip_speed;
					state <= wait_for_divider;
				
				when display_trip_speed =>
					display_row(tripspeed_location, rows_to_req);
					display_row(tripspeed_location + 1, rows_to_req);
					
					state <= wait_for_tripdata_action;
				
				when start_trip_length_div =>
					-- Select right channel for divider
					divider_sel <= "011";
					nextstate <= display_trip_length;
					
					state <= wait_for_divider;
				
				when display_trip_length =>
					display_row(trip_length_location, rows_to_req);
					display_row(trip_length_location + 1, rows_to_req);
					
					state <= wait_for_tripdata_action;
				
				when start_trip_time_div =>
					divider_sel <= "010";
					nextstate <= display_trip_time;
					
					state <= wait_for_divider;
				
				when display_trip_time =>
					display_row(trip_time_location, rows_to_req);
					display_row(trip_time_location + 1, rows_to_req);
					
					state <= wait_for_tripdata_action;			
				
				when wait_for_tripdata_action =>
					if switch_l_sync = '1' then
						-- Check if we can move left
						case active_row is
							when 1 =>
								active_row <= active_row - 1;
								state <= start_trip_speed_div;
							when 2 =>
								active_row <= active_row - 1;
								state <= start_trip_length_div;
							when others =>
								null;
						end case;
					end if;
					
					if switch_m_sync = '1' then
						-- Go back to tripsmenu
						active_row <= 0;
						state <= display_tripsmenu;
					end if;
					
					if switch_r_sync = '1' then
						-- Check if we can move right
						case active_row is
							when 0 =>
								active_row <= active_row + 1;
								state <= start_trip_length_div;
							when 1 =>
								active_row <= active_row + 1;
								state <= start_trip_time_div;
							when others =>
								null;
						end case;
					end if;
				
				--
				-- Serial number
				-- ~~~~~~~~~~~~~
				--
				when display_serial_number =>
					display_row(serialnumber_location, rows_to_req);
					display_row(serialnumber_location + 1, rows_to_req);
					
					state <= wait_for_serialno_action;
				
				when wait_for_serialno_action =>
					if switch_m_sync = '1' then
						active_row <= 0;
						state <= display_mainmenu;
					end if;
					
				when others =>
					state <= start_speed_div;
				
			end case;

			row_req <= rows_to_req;
		end if;
	end process;
			
end architecture behavioural;

